package de.zefania.api.impl;


import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.ranges.DocumentRange;
import org.w3c.dom.ranges.Range;
import org.xml.sax.SAXException;

public class JfaniaCache {
	private String cachePath;
	private File modul;
	private File cacheDirectory;
	private Logger logger = Logger.getLogger(JfaniaCache.class);
	private DocumentBuilder builder;
	
	public JfaniaCache(final File file, final String digest, final String cachePath) throws JfaniaException {
		logger.setLevel(Level.ALL);
		this.cachePath = cachePath;
		try {			
			builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			
			if (cacheExists(file, digest)) {			  
				//Cache ist bereits angelegt (Ordner existiert und beinhaltet mehr als 1 Xml-Datei)
				logger.info("Cache wurde bereits angelegt.");
			} else { // Cache existiert nicht			
				createCache();						
			}
		} catch (Exception e) {
			throw new JfaniaException(e);
		}
	}	
	
	private Boolean cacheExists(final File modul, final String digest) {
		this.modul = modul;		
		cacheDirectory = new File(this.cachePath+digest);
		return (cacheDirectory.exists() && cacheDirectory.listFiles(new Utilities.XmlFilter()).length>1);
		//TODO Pr�fung, ob Cache angelegt ist, noch etwas unsicher
	}
	
	private void createCache()
			throws XMLStreamException, ParserConfigurationException, SAXException, IOException, TransformerException {
		logger.trace("creating Cache Directory ("+cacheDirectory+")");
		cacheDirectory.mkdirs();		
		logger.trace("start reading modul: "+modul);
		
        // Dokument parsen
        Document document = builder.parse(modul);
        
        // Rootnode des gelesenen Bibelmoduls
        // FIXME Element rootnode = document.getDocumentElement();
        NodeList ndListXmlbible = document.getElementsByTagName("XMLBIBLE");
        Node ndXmlbible = ndListXmlbible.item(0);
                    
        // Variablen f�r Gebrauch in der Schleife
        String bnumber;
        String cnumber="";        
        		
        // Liste aller Bibelb�cher-Nodes durchgehen
        NodeList ndListBiblebook = document.getElementsByTagName("BIBLEBOOK");
		for(int i=0; i<ndListBiblebook.getLength(); i++) {
            Node ndBiblebook = ndListBiblebook.item(i);
            Element elementBiblebook = (Element)ndBiblebook;            
            bnumber = elementBiblebook.getAttribute("bnumber");            
            
            copyAndWriteXmlCacheFile(ndXmlbible,ndBiblebook,bnumber,null);
            
            // Kapitel-Cache erstellen
            NodeList ndListChapter = elementBiblebook.getElementsByTagName("CHAPTER");
            for(int j=0; j<ndListChapter.getLength(); j++) {
                Node ndChapter = ndListChapter.item(j);
                Element elementChapter = (Element)ndChapter;
                
                cnumber = elementChapter.getAttribute("cnumber");
                
				copyAndWriteXmlCacheFile(ndXmlbible,ndChapter,bnumber,cnumber);
				
            }            
        }		
	}

	private void copyAndWriteXmlCacheFile(
            final Node ndXmlbible, final Node copyTree, 
	        final String bnumber, final String cnumber) throws TransformerException, IOException {
		
        // Buch bzw. Kapitel-DOM-Tree aufbauen
        Document docBiblePart = builder.newDocument();
        // XMLBIBLE-Node kopieren 
        Element root = (Element)docBiblePart.importNode(ndXmlbible, false);
        docBiblePart.appendChild(root);
        root.appendChild(docBiblePart.createElement("INFORMATION"));
        // Buch-/Kapitel-Teilbaum kopieren
        Node ndCopyTreeNew = docBiblePart.importNode(copyTree, true);
        if (copyTree.getNodeName().equals("CHAPTER")) {
            // Zu kopierender Teilbaum ist ein CHAPTER
            // => Kapitel-Cache, also muss Element <BIBLEBOOK bnumber="xxx"> noch eingef�gt werden
            // BIBLEBOOK
            Element ndBibleBook = docBiblePart.createElement("BIBLEBOOK");
            // BIBLEBOOK bnumber
            Attr atrBnumber = docBiblePart.createAttribute("bnumber");
            atrBnumber.setValue(bnumber);
            ndBibleBook.setAttributeNode(atrBnumber);
            
            root.appendChild(ndBibleBook);
            ndBibleBook.appendChild(ndCopyTreeNew);
        } else {
            root.appendChild(ndCopyTreeNew);
        }
        
        // Schreiben der XML-Datei
        OutputStream os = null;
        String filenamePrefix = bnumber+(cnumber==null || cnumber.equals("")? "" : "_"+cnumber);
        os = new FileOutputStream(new File(cacheDirectory, filenamePrefix+".xml"));
        
        TransformerFactory.newInstance().newTransformer().transform(new DOMSource(docBiblePart), new StreamResult(os));
        os.close();        
    }
	
	public final Document getChapter(final int bnumber, final int cnumber) {
		Document document=null;
		try {
			File cFile = new File(cacheDirectory, String.valueOf(bnumber)+"_"+String.valueOf(cnumber)+".xml");
			logger.debug("loading "+cFile.getAbsoluteFile());
			document = builder.parse(cFile);			
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return document;
	}
	
	public final Document getBook(final int bnumber) {
		Document document=null;
		try {
			File bFile = new File(cacheDirectory, String.valueOf(bnumber)+".xml");
			logger.debug("loading "+bFile.getAbsoluteFile());
			document = builder.parse(bFile);			
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return document;
	}
	
	
	
	public final Document getSection(final int b, final int c, final int v1, final int v2) {
		Document document=null;		
		try {
			File cFile = new File(cacheDirectory, String.valueOf(b)+"_"+String.valueOf(c)+".xml");
			logger.debug("loading "+cFile.getAbsoluteFile());
			document = builder.parse(cFile);			
	   
		    XPath xpath = XPathFactory.newInstance().newXPath();
		    XPathExpression expr1 = xpath.compile("//VERS[@vnumber='"+v1+"']");
		    XPathExpression expr2 = xpath.compile("//VERS[@vnumber='"+v2+"']");
		    Node startNode = (Node) expr1.evaluate(document, XPathConstants.NODE);
		    Node endNode = (Node) expr2.evaluate(document, XPathConstants.NODE);

		    Range r = ((DocumentRange)document).createRange();
			r.setStartBefore(startNode);
			r.setEndAfter(endNode);
			DocumentFragment section = r.cloneContents();
			Node ndXmlbible = document.getElementsByTagName( "XMLBIBLE" ).item(0);
			
			return createDocument(section, ndXmlbible, new PositionImpl(b,b,c,c,v1,v2));
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (XPathExpressionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	
	
	private Document createDocument(final DocumentFragment df, final Node ndXmlbible, final PositionImpl p) {
		Document docBiblePart = builder.newDocument();
		Element root = (Element)docBiblePart.importNode(ndXmlbible, false);
		docBiblePart.appendChild(root);				
		root.appendChild(docBiblePart.createElement("INFORMATION"));
		Element book = docBiblePart.createElement("BIBLEBOOK");
		//logger.debug(df.getTextContent());
		if(p.b1()==p.b2()) {
			book.setAttribute("bnumber", String.valueOf(p.b1()));
			root.appendChild(book);
			
			
			if (p.c1()==p.c2()) {
				Element chapter = docBiblePart.createElement("CHAPTER");
				book.appendChild(chapter);
				
				chapter.setAttribute("cnumber", String.valueOf(p.c1()));
				NodeList nodes = df.getChildNodes();
				
				for (int i = 0; i<nodes.getLength();i++) {					
					chapter.appendChild(docBiblePart.importNode(nodes.item(i), true));
				}
							
			}			
		}

		return docBiblePart;
	}
	
	
}
